package com.james.codeauto.table;

import com.james.codeauto.common.StringUtils;
import com.james.codeauto.util.GenMarkDownUtils;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.WordUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;


public class GenUtils {

    private static String currentTableName;

    public static List<String> getTemplates() {
        List<String> templates = new ArrayList<String>();
        templates.add("E.java.vm");
        templates.add("EDto.java.vm");
        templates.add("Mapper.xml.vm");

        templates.add("Service.java.vm");
        templates.add("ServiceImpl.java.vm");
        templates.add("Controller.java.vm");
        templates.add("Mapper.java.vm");
        templates.add("QueryPageDto.java.vm");

        return templates;
    }

    public static List<String> getUITemplates() {
        List<String> templates = new ArrayList<String>();
        templates.add("templatevm/add.html.vm");
        templates.add("templatevm/list.html.vm");
        templates.add("templatevm/api.md.vm");
        return templates;
    }

    public static List<String> getTablesTemplates() {
        List<String> templates = new ArrayList<String>();
        templates.add("templatevm/table.vm");
        return templates;
    }


    public static String generatorAllTablesCode(Map<String, String> table,
                                                List<Map<String, String>> columns) throws Exception {
        //配置信息
        Map<String, Object> data = getTableEntity(table, columns, true);
        //TableEntity tableEntity = (TableEntity) data.get("tableEntity");
        VelocityContext context = (VelocityContext) data.get("context");
        //Configuration config = getConfig();
        StringWriter sw = new StringWriter();
        Template tpl = Velocity.getTemplate("templatevm/table.vm", "UTF-8");
        tpl.merge(context, sw);
        return sw.toString();
    }



    public static Map<String, Object> getTableEntity(Map<String, String> table, List<Map<String, String>> columns, boolean tables) throws Exception {
        //配置信息
        Configuration config = getConfig();
        boolean hasBigDecimal = false;
        boolean hasList = false;
        //表信息
        TableEntity tableEntity = new TableEntity();
        tableEntity.setTableName(table.get("tablename"));
        tableEntity.setComments(table.get("tablecomment"));
        //表名转换成Java类名
        String className = tableToJava(tableEntity.getTableName(), config.getStringArray("tablePrefix"), tables);
        tableEntity.setClassName(className);
        tableEntity.setClassname(className.substring(0, 1).toLowerCase() + className.substring(1));

        //列信息
        List<ColumnEntity> columsList = new ArrayList<>();
        for (Map<String, String> column : columns) {
            ColumnEntity columnEntity = new ColumnEntity();
            columnEntity.setColumnName(column.get("columnname"));
            columnEntity.setDataType(column.get("datatype"));
            columnEntity.setIsNullable(column.get("isnullable"));
            columnEntity.setColumnDefault(column.get("columndefault"));
            if ("PRI".equalsIgnoreCase(column.get("columnkey")) && tableEntity.getPk() == null) {
                columnEntity.setComments("主键");
                columnEntity.setIsNullable("YES");
                columnEntity.setColumnDefault("uuid");
            } else {
                columnEntity.setComments(column.get("columncomment"));
            }

            columnEntity.setExtra(column.get("extra"));


            Object bigInteger = column.get("charactermaximumlength");
            System.out.println(bigInteger);
            if (bigInteger == null) {
                columnEntity.setCharacterMaximumLength("--");
            } else {
                columnEntity.setCharacterMaximumLength("" + bigInteger);
            }


            //列名转换成Java属性名
            String attrName = columnToJava(columnEntity.getColumnName(), tables);
            columnEntity.setAttrName(attrName);
            columnEntity.setAttrname(attrName.substring(0, 1).toLowerCase() + attrName.substring(1));

            //列的数据类型，转换成Java类型
            String attrType = config.getString(columnEntity.getDataType(), columnToJava(columnEntity.getDataType(), tables));
            columnEntity.setAttrType(attrType);


            if (!hasBigDecimal && attrType.equals("BigDecimal")) {
                hasBigDecimal = true;
            }
            if (!hasList && "array".equals(columnEntity.getExtra())) {
                hasList = true;
            }
            //是否主键
            if ("PRI".equalsIgnoreCase(column.get("columnkey")) && tableEntity.getPk() == null) {
                tableEntity.setPk(columnEntity);
            }

            columsList.add(columnEntity);
        }
        tableEntity.setColumns(columsList);

        //没主键，则第一个字段为主键
        if (tableEntity.getPk() == null) {
            tableEntity.setPk(tableEntity.getColumns().get(0));
        }

        //设置velocity资源加载器
        Properties props = new Properties();
        // props.put("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
        props.put("file.resource.loader.path", Paths.get("templatevm").toAbsolutePath().toString());
        Velocity.init(props);
        String mainPath = config.getString("mainPath");
        mainPath = !StringUtils.isNotBlank(mainPath) ? "com.james" : mainPath;
        Map<String, Object> map = new HashMap<>();
        //维护变量
        map.put("tableName", tableEntity.getTableName());
        map.put("comments", tableEntity.getComments());
        map.put("pk", tableEntity.getPk());
        map.put("className", tableEntity.getClassName());
        map.put("classname", tableEntity.getClassname());
        map.put("pathName", tableEntity.getClassname().toLowerCase());
        map.put("columns", tableEntity.getColumns());
        map.put("hasBigDecimal", hasBigDecimal);
        map.put("hasList", hasList);
        map.put("mainPath", mainPath);
        map.put("package", config.getString("package"));
        map.put("moduleName", config.getString("moduleName"));
        map.put("author", config.getString("author"));
        map.put("email", config.getString("email"));
        map.put("datetime", GenMarkDownUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
        VelocityContext context = new VelocityContext(map);
        Map<String, Object> result = new HashMap<>();
        result.put("tableEntity", tableEntity);
        result.put("context", context);
        return result;
    }

    public static void main(String args[]) {
        try {
            GenUtils.getTableCode("coupon_batch");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static String getTableCode(String tableName) throws Exception {
        //查询表信息
        String tableStr = "";
        Map<String, String> table = queryTable(tableName);
        //查询列信息
        List<Map<String, String>> columns = queryColumns(tableName);
        //生成代码
        try {
            GenUtils.generatorCode(table, columns);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return tableStr;
    }

    static Connection conn = null;

    private static Connection getConnection() throws Exception {

        if (conn != null && !conn.isClosed()) {
            return conn;
        }

        String driver = "com.mysql.cj.jdbc.Driver";        // "oracle.jdbc.driver.OracleDriver";
        String url = "jdbc:mysql://10.39.82.23:48531/ctu_promotion?useUnicode=true&useSSL=false&characterEncoding=UTF-8&allowMultiQueries=true";
        String userName = "ctu_promotion_rw";
        String password = ",%{rq7m5[W/K";

//        url = "jdbc:mysql://10.39.87.31:3306/marketing?useUnicode=true&useSSL=false&characterEncoding=UTF-8&allowMultiQueries=true";
//        userName = "root";
//        password = "123456";


        Class.forName(driver);

        DriverManager.setLoginTimeout(30000);
        conn = DriverManager.getConnection(url, userName, password);


        return conn;
    }

    public static Map<String, String> queryTable(String tableName) throws Exception {
        String sqlStr = "select table_name tableName, engine, table_comment tableComment, create_time createTime from information_schema.tables where table_schema = (select database()) and table_name = '" + tableName + "'";
        Statement stmt = null;
        ResultSet res = null;
        HashMap<String, String> data = new HashMap<>();
        Connection conn = getConnection();

        stmt = conn.prepareStatement(sqlStr);
        //stmt = conn.createStatement();
        res = stmt.executeQuery(sqlStr);
        while (res.next()) {
            data.put("tablename",res.getString("tableName"));
            data.put("engine",res.getString("engine"));
            data.put("tablecomment",res.getString("tableComment"));
            data.put("createtime",res.getString("createTime"));
        }
        res.close();
        stmt.close();
        return data;
    }

    public static List<Map<String, String>> queryColumns(String tableName) throws Exception {
        String sqlStr = "select  is_nullable isNullable,column_default columnDefault, character_maximum_length characterMaximumLength,  column_name columnName, data_type dataType, column_comment columnComment, " +
                "column_key columnKey, extra from information_schema.columns where table_name = '" + tableName + "' and table_schema = (select database()) order by ordinal_position";
        List<Map<String, String>> items = new ArrayList<>();

        Connection conn = getConnection();
        Statement stmt = null;
        ResultSet res = null;
        stmt = conn.prepareStatement(sqlStr);
        //stmt = conn.createStatement();
        res = stmt.executeQuery(sqlStr);
        while (res.next()) {
            HashMap<String, String> data = new HashMap<>();
            data.put("isnullable",res.getString("isNullable"));
            data.put("columncomment",res.getString("columnComment"));
            data.put("columndefault",res.getString("columnDefault"));
            data.put("extra",res.getString("extra"));
            data.put("columnkey",res.getString("columnKey"));
            data.put("datatype",res.getString("dataType"));
            data.put("columnname",res.getString("columnName"));
            data.put("charactermaximumlength",res.getString("characterMaximumLength"));
            items.add(data);
        }
        res.close();
        stmt.close();
        return items;
    }

    /**
     * 生成代码
     */
    public static void generatorCode(Map<String, String> table,
                                     List<Map<String, String>> columns) throws Exception {


        //获取模板列表
        Map<String, Object> data = getTableEntity(table, columns, false);
        TableEntity tableEntity = (TableEntity) data.get("tableEntity");
        VelocityContext context = (VelocityContext) data.get("context");
        Configuration config = getConfig();
        List<String> templates = getTemplates();
        for (String template : templates) {
            //渲染模板
            StringWriter sw = new StringWriter();
            Template tpl = Velocity.getTemplate(template, "UTF-8");
            tpl.merge(context, sw);
            String fileName = getFileName(template, tableEntity.getClassName(), config.getString("package"), config.getString("moduleName"));
            if (!new File(fileName).exists()) {
                new File(new File(fileName).getParent()).mkdirs();
                new File(fileName).createNewFile();
            }
            try {
                Files.write(Paths.get(fileName), sw.toString().getBytes(StandardCharsets.UTF_8));
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
//            try {
//                //添加到zip
//                zip.putNextEntry(new ZipEntry(getFileName(template, tableEntity.getClassName(), config.getString("package"), config.getString("moduleName"))));
//                IOUtils.write(sw.toString(), zip, "UTF-8");
//                IOUtils.closeQuietly(sw);
//                zip.closeEntry();
//            } catch (IOException e) {
//                throw new Exception("渲染模板失败，表名：" + tableEntity.getTableName());
//            }
        }
    }


    /**
     * 列名转换成Java属性名
     */
    public static String columnToJava(String columnName, boolean orignalTablesName) {
        if (orignalTablesName) {
            return columnName;
        }
        return WordUtils.capitalizeFully(columnName, new char[]{'_'}).replace("_", "");
    }

    /**
     * 表名转换成Java类名
     */
    public static String tableToJava(String tableName, String[] tablePrefixArray, boolean orignalTablesName) {
        if (null != tablePrefixArray && tablePrefixArray.length > 0) {
            for (String tablePrefix : tablePrefixArray) {
                if (tableName.startsWith(tablePrefix)) {
                    tableName = tableName.replaceFirst(tablePrefix, "");
                }
            }
        }
        if (orignalTablesName) {
            return tableName;
        }
        return columnToJava(tableName, orignalTablesName);
    }

    /**
     * 获取配置信息
     */
    public static Configuration getConfig() {
        try {
            PropertiesConfiguration configuration = new PropertiesConfiguration();
            String path = Paths.get("", "generator.properties").toAbsolutePath().toString();
            configuration.load(new FileInputStream(path));
            return configuration;
        } catch (ConfigurationException | FileNotFoundException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 获取文件名
     */
    public static String getFileName(String template, String className, String packageName, String moduleName) {
        String packagePath = "src" + File.separator + "main" + File.separator + "java" + File.separator;
        if (StringUtils.isNotBlank(packageName)) {
            packagePath += packageName.replace(".", File.separator) + File.separator + moduleName + File.separator;
        }

        if (template.contains("add.html.vm")) {
            return packagePath + "ui" + File.separator + className + File.separator + "add.html";
        }
        if (template.contains("list.html.vm")) {
            return packagePath + "ui" + File.separator + className + File.separator + "list.html";
        }

        if (template.contains("api.md.vm")) {
            return packagePath + "ui" + File.separator + className + "api.md";
        }

        if (template.contains("table.vm")) {
            return packagePath + "db" + File.separator + className + "table.txt";
        }

        if (template.equals("E.java.vm")) {
            return packagePath + "entity" + File.separator + className + ".java";
        }

        if (template.equals("EDto.java.vm")) {
            return packagePath + "entity" + File.separator + className + "Dto.java";
        }

        if (template.contains("Mapper.java.vm")) {
            return packagePath + "mapper" + File.separator + className + "Mapper.java";
        }


        if (template.contains("QueryPageDto.java.vm")) {
            return packagePath + "requestDto" + File.separator + className + "QueryParamDto.java";
        }



        if (template.contains("Service.java.vm")) {
            return packagePath + "service" + File.separator + className + "Service.java";
        }

        if (template.contains("ServiceImpl.java.vm")) {
            return packagePath + "service" + File.separator + "impl" + File.separator + className + "ServiceImpl.java";
        }

        if (template.contains("Controller.java.vm")) {
            return packagePath + "controller" + File.separator + className + "Controller.java";
        }

        if (template.contains("Mapper.xml.vm")) {
            return packagePath + "mapper" + File.separator + className + "Mapper.xml";
        }


        return null;
    }

    private static String splitInnerName(String name) {
        name = name.replaceAll("\\.", "_");
        return name;
    }
}
